package com.gitee.wsl.ext.array

import com.gitee.wsl.ext.bitcode.lowHigh
import com.gitee.wsl.ext.bitcode.shr
import com.gitee.wsl.lang.ByteOrder


// ********************************************** 写 **********************************************
fun ByteArray.writeInt8(value: Int, offset: Int = 0): ByteArray {
    throwOffestError(this, offset)
    // 无符号Int 执行写入
    this[offset] = value.toByte()
    return this
}

fun ByteArray.writeInt16BE(value: Int, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, 2)
    // 执行写入
    this[offset] = (value and 0xff00 ushr 8).toByte()
    this[offset + 1] = (value and 0xff).toByte()
    return this
}

fun ByteArray.writeInt16LE(value: Int, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, 2)
    // 无符号Int 执行写入
    this[offset] = (value and 0xff).toByte()
    this[offset + 1] = (value and 0xff00 ushr 8).toByte()
    return this
}

fun ByteArray.writeInt32BE(value: Long, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, 4)
    // 无符号Int 执行写入
    this[offset + 3] = (value and 0xff).toByte()
    this[offset + 2] = (value and 0xff00 ushr 8).toByte()
    this[offset + 1] = (value and 0xff0000 ushr 16).toByte()
    this[offset] = (value and 0xff000000 ushr 24).toByte()
    return this
}

fun ByteArray.writeInt32LE(value: Long, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, 4)
    // 无符号Int 执行写入
    this[offset] = (value and 0xff).toByte()
    this[offset + 1] = (value and 0xff00 ushr 8).toByte()
    this[offset + 2] = (value and 0xff0000 ushr 16).toByte()
    this[offset + 3] = (value and 0xff000000 ushr 24).toByte()
    return this
}

// 写入Float类型
fun ByteArray.writeFloatBE(value: Float, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, 4)
    this.writeInt32BE(value.toBits().toLong(), offset)
    return this
}

fun ByteArray.writeFloatLE(value: Float, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, 4)
    this.writeInt32LE(value.toBits().toLong(), offset)
    return this
}

// 写入Double类型
fun ByteArray.writeDoubleBE(value: Double, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, Double.SIZE_BYTES)
    this.writeLongBE(value.toBits(), offset)
    return this
}

fun ByteArray.writeDoubleLE(value: Double, offset: Int = 0): ByteArray {
    throwOffestError(this, offset, Double.SIZE_BYTES)
    this.writeLongLE(value.toBits(), offset)
    return this
}


fun ByteArray.writeByteArray(start: Int, bytes: ByteArray) {
    val end = (start + bytes.size).coerceAtMost(size)
    for (i in start until end) {
        this[i] = bytes[i - start]
    }
}

/**
 * Writes a long split into 8 bytes.
 * @param [offset] start index
 * @param [value] the value to insert
 * Thanks to manu0466
 */
// fun ByteArray.writeLongLE(value: Long, offset: Int) {
//    for (i in 7 downTo 0) {
//        val temp = (value ushr (i * 8)).toUByte()
//        this[offset + 7 - i] = temp.toByte()
//    }
//}

fun ByteArray.writeLongBE(value: Long, offset: Int) {
    val (lo,hi) = value.lowHigh
    writeInt32BE(hi.toLong(), offset)
    writeInt32BE(lo.toLong(), offset + 4)
//    this[offset] = (value shr 56).toByte()
//    this[offset + 1] = (value shr 48).toByte()
//    this[offset + 2] = (value shr 40).toByte()
//    this[offset + 3] = (value shr 32).toByte()
//    this[offset + 4] = (value shr 24).toByte()
//    this[offset + 5] = (value shr 16).toByte()
//    this[offset + 6] = (value shr 8).toByte()
//    this[offset + 7] = value.toByte()
}

fun ByteArray.writeLongLE(value: Long, offset: Int) {
    val (lo,hi) = value.lowHigh
    writeInt32LE(lo.toLong(), offset)
    writeInt32LE(hi.toLong(), offset + 4)
//    this[offset] = (value shr 56).toByte()
//    this[offset + 1] = (value shr 48).toByte()
//    this[offset + 2] = (value shr 40).toByte()
//    this[offset + 3] = (value shr 32).toByte()
//    this[offset + 4] = (value shr 24).toByte()
//    this[offset + 5] = (value shr 16).toByte()
//    this[offset + 6] = (value shr 8).toByte()
//    this[offset + 7] = value.toByte()
}


fun ByteArray.writeByte(byte: Byte, offset: Int = 0) {
    this[offset] = byte
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeByte(byte: Byte, offset: Int = 0) {
    this[offset] = byte.toUByte()
}


fun ByteArray.writeUByte(ubyte: UByte, offset: Int = 0) {
    this[offset] = ubyte.toByte()
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeUByte(ubyte: UByte, offset: Int = 0) {
    this[offset] = ubyte
}


fun ByteArray.writeShort(short: Short, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = writeUShort(short.toUShort(), offset, byteOrder)

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeShort(short: Short, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = writeUShort(short.toUShort(), offset, byteOrder)



fun ByteArray.writeUShort(ushort: UShort, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) {
    when (byteOrder) {
        ByteOrder.LittleEndian -> {
            writeUByte(ushort.toUByte(), offset)
            writeUByte((ushort shr 8).toUByte(), offset + 1)
        }
        ByteOrder.BigEndian -> {
            writeUByte((ushort shr 8).toUByte(), offset)
            writeUByte(ushort.toUByte(), offset + 1)
        }
    }
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeUShort(ushort: UShort, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) {
    when (byteOrder) {
        ByteOrder.LittleEndian -> {
            writeUByte(ushort.toUByte(), offset)
            writeUByte((ushort shr 8).toUByte(), offset + 1)
        }
        ByteOrder.BigEndian -> {
            writeUByte((ushort shr 8).toUByte(), offset)
            writeUByte(ushort.toUByte(), offset + 1)
        }
    }
}


fun ByteArray.writeInt(int: Int, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = writeUInt(int.toUInt(), offset, byteOrder)

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeInt(int: Int, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = writeUInt(int.toUInt(), offset, byteOrder)



fun ByteArray.writeUInt(uint: UInt, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) {
    when (byteOrder) {
        ByteOrder.LittleEndian -> {
            writeUShort(uint.toUShort(), offset)
            writeUShort((uint shr 16).toUShort(), offset + 2)
        }
        ByteOrder.BigEndian -> {
            writeUShort((uint shr 16).toUShort(), offset)
            writeUShort(uint.toUShort(), offset + 2)
        }
    }
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeUInt(uint: UInt, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) {
    when (byteOrder) {
        ByteOrder.LittleEndian -> {
            writeUShort(uint.toUShort(), offset)
            writeUShort((uint shr 16).toUShort(), offset + 2)
        }
        ByteOrder.BigEndian -> {
            writeUShort((uint shr 16).toUShort(), offset)
            writeUShort(uint.toUShort(), offset + 2)
        }
    }
}


fun ByteArray.writeLong(long: Long, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = writeULong(long.toULong(), offset, byteOrder)

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeLong(long: Long, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = writeULong(long.toULong(), offset, byteOrder)



fun ByteArray.writeULong(ulong: ULong, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) {
    when (byteOrder) {
        ByteOrder.LittleEndian -> {
            writeUInt(ulong.toUInt(), offset)
            writeUInt((ulong shr 32).toUInt(), offset + 4)
        }
        ByteOrder.BigEndian -> {
            writeUInt((ulong shr 32).toUInt(), offset)
            writeUInt(ulong.toUInt(), offset + 4)
        }
    }
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.writeULong(ulong: ULong, offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) {
    when (byteOrder) {
        ByteOrder.LittleEndian -> {
            writeUInt(ulong.toUInt(), offset)
            writeUInt((ulong shr 32).toUInt(), offset + 4)
        }
        ByteOrder.BigEndian -> {
            writeUInt((ulong shr 32).toUInt(), offset)
            writeUInt(ulong.toUInt(), offset + 4)
        }
    }
}
